#include "ISPMCommon.h"
#include "Application.h"
#include "D3DDevice.h"
#include <vector>

#define GET_IMMEDIATE_CONTEXT(D3DDevice) (D3DDevice.m_pImmediateContext)
#define GET_DEVICE(D3DDevice) (D3DDevice.m_pd3dDevice)
#define GET_SWAPCHAIN(D3DDevice) (D3DDevice.m_pSwapChain)

//forward declaration
LRESULT CALLBACK    WndProc( HWND, UINT, WPARAM, LPARAM );

HRESULT CApplication::InitApp( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
        return E_FAIL;

    if( FAILED( InitDevices() ) )
    {
        CleanupDevice();
        return E_FAIL;
    }

    return S_OK;
}

HRESULT CApplication::InitWindow( HINSTANCE hInstance, int nCmdShow )
{
    // Register class
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof( WNDCLASSEX );
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )"Jenova" );
    wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = L"TutorialWindowClass";
    wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )"Jenova" );
    if( !RegisterClassEx( &wcex ) )
        return E_FAIL;

    // Create window
    m_hInst = hInstance;
    RECT rc = { 0, 0, 1024, 768 };
    AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
    m_hWnd = CreateWindow( L"TutorialWindowClass", L"Direct3D 11 Tutorial 1: Direct3D 11 Basics", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance,
        NULL );
    if( !m_hWnd )
        return E_FAIL;

    ShowWindow( m_hWnd, nCmdShow );

    return S_OK;
}

HRESULT CApplication::InitDevices()
{

    HRESULT hr = S_OK;
    //finding the video hardware present on the computer

    //creating the DXGI factory
    IDXGIFactory * pFactory;
    hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&pFactory) );

    // checking the available adapters on the system
    UINT i = 0; 
    IDXGIAdapter * pAdapter; 
    std::vector <IDXGIAdapter*> vAdapters; 
    while(pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND) 
    { 
        vAdapters.push_back(pAdapter); 
        ++i; 
    } 

    RECT rc;
    GetClientRect( m_hWnd, &rc );
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory( &sd, sizeof( sd ) );
    sd.BufferCount = 1;
    sd.BufferDesc.Width = width;
    sd.BufferDesc.Height = height;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = m_hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;

    m_MainDevice.InitDevice(&sd);
    m_SecondaryDevice.InitDevice(NULL);

    // Create a render target view
    ID3D11Texture2D* pBackBuffer = NULL;
    hr = GET_SWAPCHAIN(m_MainDevice)->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
    if( FAILED( hr ) )
        return hr;

    hr = GET_DEVICE(m_MainDevice)->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView );
    pBackBuffer->Release();
    if( FAILED( hr ) )
        return hr;

    GET_IMMEDIATE_CONTEXT(m_MainDevice)->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL );

    // Setup the viewport
    D3D11_VIEWPORT vp;
    vp.Width = (FLOAT)width;
    vp.Height = (FLOAT)height;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    GET_IMMEDIATE_CONTEXT(m_MainDevice)->RSSetViewports( 1, &vp );
}

//windows main loop 
void CApplication::MainLoop( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    // Main message loop
    MSG msg = {0};
    while( WM_QUIT != msg.message )
    {
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            Render();
        }
    }
}

//Main render function
void CApplication::Render()
{
    // Just clear the backbuffer
    float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha
    GET_IMMEDIATE_CONTEXT(m_MainDevice)->ClearRenderTargetView( m_pRenderTargetView, ClearColor );
}

void CApplication::CleanupDevice()
{
    throw std::exception("The method or operation is not implemented.");
}
